package ElectricCraft.model;

import java.util.Arrays;

/**
 * ModelExecutionState class.
 */
public final class ModelExecutionState {
    public static final class GroupInfo {
        public final int [] output;
        public final int [] conditional;
        public final int [] keys;

        public GroupInfo(int [] output, int [] conditional, int [] keys) {
            this.output = output;
            this.conditional = conditional;
            this.keys = keys;
        }
    }

    public static final class KeyInfo {
        public int group;
        public boolean defaultState;

        public KeyInfo(int group, boolean defaultState) {
            this.group = group;
            this.defaultState = defaultState;
        }
    }

    public final ModelState     state;
    public final GroupInfo []   groups;
    public final KeyInfo []     keys;
    public final int []         inputs;
    public final int []         outputs;

    public final boolean [] inputState;

    public ModelExecutionState(GroupInfo [] groups, KeyInfo [] keys, int [] inputs, int [] outputs) {
        this.state      = new ModelState(groups.length, keys.length);
        this.groups     = groups;
        this.keys       = keys;
        this.inputs     = inputs;
        this.outputs    = outputs;
        this.inputState = new boolean[inputs.length];

        // initialize keys by default states
        for (int k = 0; k < keys.length; ++k) {
            state.keys[k] = keys[k].defaultState;
        }
    }

    public void setInput(int index, boolean state) {
        inputState[index] = state;
    }

    public boolean getInput(int index) {
        return inputState[index];
    }

    public int getInputsCount() {
        return inputs.length;
    }

    public boolean getOutput(int index) {
        int group = outputs[index];
        if (group == -1) {
            return false;
        }

        return state.groups[group];
    }

    public void step() {
        updateKeys();

        updateGroups();
    }

    private void updateKeys() {
        for (int index = 0; index < keys.length; ++index) {
            KeyInfo keyInfo = keys[index];
            state.keys[index] = state.groups[keyInfo.group] ? !keyInfo.defaultState : keyInfo.defaultState;
        }
    }

    private void updateGroups() {
        Arrays.fill(state.groups, false);

        powerGroup(0); // power group

        for (int k = 0; k < inputs.length; ++k) {
            if (inputState[k]) {
                powerGroup(inputs[k]);
            }
        }
    }

    private void powerGroup(int group) {
        state.groups[group] = true;

        GroupInfo groupInfo = groups[group];

        for (int outputGroup : groupInfo.output) {
            if (!state.groups[outputGroup]) {
                powerGroup(outputGroup);
            }
        }

        for (int k = 0; k < groupInfo.conditional.length; ++k) {
            int outputGroup = groupInfo.conditional[k];
            int key         = groupInfo.keys[k];

            if (state.keys[key] && !state.groups[outputGroup]) {
                powerGroup(outputGroup);
            }
        }
    }
}
